// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/address_list.h"

#include "base/strings/string_util.h"
#include "base/sys_byteorder.h"
#include "net/base/ip_address.h"
#include "net/base/sockaddr_storage.h"
#include "net/base/sys_addrinfo.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {
namespace {

    const char kCanonicalHostname[] = "canonical.bar.com";

    TEST(AddressListTest, Canonical)
    {
        // Create an addrinfo with a canonical name.
        struct sockaddr_in address;
        // The contents of address do not matter for this test,
        // so just zero-ing them out for consistency.
        memset(&address, 0x0, sizeof(address));
        // But we need to set the family.
        address.sin_family = AF_INET;
        struct addrinfo ai;
        memset(&ai, 0x0, sizeof(ai));
        ai.ai_family = AF_INET;
        ai.ai_socktype = SOCK_STREAM;
        ai.ai_addrlen = sizeof(address);
        ai.ai_addr = reinterpret_cast<sockaddr*>(&address);
        ai.ai_canonname = const_cast<char*>(kCanonicalHostname);

        // Copy the addrinfo struct into an AddressList object and
        // make sure it seems correct.
        AddressList addrlist1 = AddressList::CreateFromAddrinfo(&ai);
        EXPECT_EQ("canonical.bar.com", addrlist1.canonical_name());

        // Copy the AddressList to another one.
        AddressList addrlist2 = addrlist1;
        EXPECT_EQ("canonical.bar.com", addrlist2.canonical_name());
    }

    TEST(AddressListTest, CreateFromAddrinfo)
    {
        // Create an 4-element addrinfo.
        const unsigned kNumElements = 4;
        SockaddrStorage storage[kNumElements];
        struct addrinfo ai[kNumElements];
        for (unsigned i = 0; i < kNumElements; ++i) {
            struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(storage[i].addr);
            storage[i].addr_len = sizeof(struct sockaddr_in);
            // Populating the address with { i, i, i, i }.
            memset(&addr->sin_addr, i, IPAddress::kIPv4AddressSize);
            addr->sin_family = AF_INET;
            // Set port to i << 2;
            addr->sin_port = base::HostToNet16(static_cast<uint16_t>(i << 2));
            memset(&ai[i], 0x0, sizeof(ai[i]));
            ai[i].ai_family = addr->sin_family;
            ai[i].ai_socktype = SOCK_STREAM;
            ai[i].ai_addrlen = storage[i].addr_len;
            ai[i].ai_addr = storage[i].addr;
            if (i + 1 < kNumElements)
                ai[i].ai_next = &ai[i + 1];
        }

        AddressList list = AddressList::CreateFromAddrinfo(&ai[0]);

        ASSERT_EQ(kNumElements, list.size());
        for (size_t i = 0; i < list.size(); ++i) {
            EXPECT_EQ(ADDRESS_FAMILY_IPV4, list[i].GetFamily());
            // Only check the first byte of the address.
            EXPECT_EQ(i, list[i].address().bytes()[0]);
            EXPECT_EQ(static_cast<int>(i << 2), list[i].port());
        }

        // Check if operator= works.
        AddressList copy;
        copy = list;
        ASSERT_EQ(kNumElements, copy.size());

        // Check if copy is independent.
        copy[1] = IPEndPoint(copy[2].address(), 0xBEEF);
        // Original should be unchanged.
        EXPECT_EQ(1u, list[1].address().bytes()[0]);
        EXPECT_EQ(1 << 2, list[1].port());
    }

    TEST(AddressListTest, CreateFromIPAddressList)
    {
        struct TestData {
            std::string ip_address;
            const char* in_addr;
            int ai_family;
            size_t ai_addrlen;
            size_t in_addr_offset;
            size_t in_addr_size;
        } tests[] = {
            {
                "127.0.0.1",
                "\x7f\x00\x00\x01",
                AF_INET,
                sizeof(struct sockaddr_in),
                offsetof(struct sockaddr_in, sin_addr),
                sizeof(struct in_addr),
            },
            {
                "2001:db8:0::42",
                "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42",
                AF_INET6,
                sizeof(struct sockaddr_in6),
                offsetof(struct sockaddr_in6, sin6_addr),
                sizeof(struct in6_addr),
            },
            {
                "192.168.1.1",
                "\xc0\xa8\x01\x01",
                AF_INET,
                sizeof(struct sockaddr_in),
                offsetof(struct sockaddr_in, sin_addr),
                sizeof(struct in_addr),
            },
        };
        const std::string kCanonicalName = "canonical.example.com";

        // Construct a list of ip addresses.
        IPAddressList ip_list;
        for (size_t i = 0; i < arraysize(tests); ++i) {
            IPAddress ip_address;
            ASSERT_TRUE(ip_address.AssignFromIPLiteral(tests[i].ip_address));
            ip_list.push_back(ip_address);
        }

        AddressList test_list = AddressList::CreateFromIPAddressList(ip_list,
            kCanonicalName);
        std::string canonical_name;
        EXPECT_EQ(kCanonicalName, test_list.canonical_name());
        EXPECT_EQ(arraysize(tests), test_list.size());
    }

} // namespace
} // namespace net
